/**
 * 210508：修正射线检测时计算交点使用了没有经变换的射线的bug
 * 210824：升级修改【three.js r120-r131】
 */

let THREE=require('three');
import Collider from './collider';
import CONST from '../collision-const';

/**
 * 胶囊体
 * @param values {color,radius,halfHeight} color:THREE.Color,radius:半径,halfHeight:高度的一半
 */
export default class CapsuleCollider extends Collider{
    constructor(values={}){
        super(values);
    }

    //------------------------------------------对外属性及方法--------------------------------------------

    /**
     * 获取类型
     * @returns {string}
     */
    get colliderType(){
        return CONST.COLLIDER_TYPE_CAPSULE;
    }

    /**
     * 获取包围盒（世界空间）
     * @returns {this}
     */
    get bounds(){
        return this._box.clone().applyMatrix4(this.matrixWorld);
    }

    /**
     * 获取包围盒（局部空间）
     * @returns {box}
     */
    get box(){
        return this._box.clone();
    }

    /**
     * 获取包围球（局部空间）
     * @returns {Sphere}
     */
    get sphere(){
        return this._sphere.clone();
    }

    /**
     * 获取半径
     * @returns {*}
     */
    get radius(){
        return this._props.radius;
    }

    /**
     * 设置半径
     * @param radius
     */
    set radius(radius){
        if(radius===this._props.radius)return;
        this._props.radius=radius;
        let h=this._props.halfHeight;
        let r=this._props.radius;
        this._box.min.set(-r,-h-r,-r);
        this._box.max.set(r,h+r,r);
        this._sphere.radius=h+r;
        if(this._debug) {
            this._debug.children[0].scale.set(r, h, r);
            this._debug.children[1].scale.set(r, r, r);
            this._debug.children[2].scale.set(r, r, r);
        }
    }

    /**
     * 获取底部中心点（局部空间）
     * @returns {Vector3}
     */
    get bottomCenter(){
        return new THREE.Vector3().copy(this._bottomCenter);
    }

    /**
     * 获取顶部中心点（局部空间）
     * @returns {Vector3}
     */
    get topCenter(){
        return new THREE.Vector3().copy(this._topCenter);
    }

    /**
     * 获取颜色
     * @returns {*}
     */
    get color(){
        return this._props.color;
    }

    /**
     * 设置颜色
     * @param color
     */
    set color(color){
        let c=this._props.color;
        if(c.r===color.r&&c.g===color.g&&c.b===color.b)return;
        this._props.color.copy(color);
        if(this._debug){
            this._debug.traverse((c)=>{
                if(c instanceof THREE.Line){
                    c.material.color=this._props.color;
                }
            });
        }
    }

    /**
     * 获取半高
     * @returns {*}
     */
    get halfHeight(){
        return this._props.halfHeight;
    }

    /**
     * 设置半高
     * @param val
     */
    set halfHeight(val){
        if(val===this._props.halfHeight)return;
        this._props.halfHeight=val;
        let h=this._props.halfHeight;
        let r=this._props.radius;
        this._bottomCenter.set(0,-h,0);
        this._topCenter.set(0,h,0);
        this._box.min.set(-r,-h-r,-r);
        this._box.max.set(r,h+r,r);
        this._sphere.radius=h+r;
        if(this._debug){
            this._debug.children[0].scale.set(r,h,r);
            this._debug.children[1].position.y=-h;
            this._debug.children[2].position.y=h;
        }
    }

    /**
     * 销毁
     */
    dispose(){
        super.dispose();
        this._box=null;
        this._sphere=null;
        this._bottomCenter=null;
        this._topCenter=null;
        this._rayPoint=null;
        this._segPoint=null;
        this._invMat=null;
        this._ray=null;
        this._tV0=null;
        this._tS0=null;
    }

    //---------------------------------------------私有属性及方法-------------------------------------------

    /**
     * 绘制调试
     * @private
     */
    _createDebug(){
        if(this._debug)return;
        this._debug=new THREE.Object3D();

        let side=new THREE.Object3D();

        //20210824
        // let g=new THREE.Geometry();
        // g.vertices.push(new THREE.Vector3(-1,-1,0),new THREE.Vector3(-1,1,0));
        let g=new THREE.BufferGeometry();
        g.setAttribute('position',new THREE.BufferAttribute(new Float32Array([-1,-1,0,-1,1,0]),3));

        let mat=new THREE.LineBasicMaterial({color:this._props.color});
        let m=new THREE.Line(g,mat);
        side.add(m);
        let d90=Math.PI*0.5;
        let m1=m.clone();
        m1.rotation.y=d90;
        side.add(m1);
        let m2=m.clone();
        m2.rotation.y=d90*2;
        side.add(m2);
        let m3=m.clone();
        m3.rotation.y=d90*3;
        side.add(m3);

        //20210824
        // let g1=new THREE.Geometry();
        // let u=Math.PI*2/35;
        // for(let i=0;i<36;i++){
        //     g1.vertices.push(new THREE.Vector3(Math.sin(u*i),Math.cos(u*i),0));
        // }
        let g1=new THREE.BufferGeometry();
        let u=Math.PI*2/35;
        let ps=new Float32Array(36*3);
        for(let i=0;i<36;i++){
            ps[i*3]=Math.sin(u*i);
            ps[i*3+1]=Math.cos(u*i);
            ps[i*3+2]=0;
        }
        g1.setAttribute('position',new THREE.BufferAttribute(ps,3));

        let c=new THREE.Line(g1,mat);
        c.rotation.x=d90;
        c.position.y=1;
        side.add(c);
        let c1=c.clone();
        c1.rotation.x=d90;
        c1.position.y=-1;
        side.add(c1);
        this._debug.add(side);

        let bottom=new THREE.Object3D();

        //20210824
        // let gc=new THREE.Geometry();
        // u=Math.PI/17;
        // for(let i=0;i<18;i++){
        //     gc.vertices.push(new THREE.Vector3(Math.sin(d90+u*i),Math.cos(d90+u*i),0));
        // }
        let gc=new THREE.BufferGeometry();
        u=Math.PI/17;
        ps=new Float32Array(18*3);
        for(let i=0;i<18;i++){
            ps[i*3]=Math.sin(d90+u*i);
            ps[i*3+1]=Math.cos(d90+u*i);
            ps[i*3+2]=0;
        }

        let b=new THREE.Line(gc,mat);
        bottom.add(b);
        let b1=b.clone();
        b1.rotation.y=d90;
        bottom.add(b1);
        bottom.position.y=-1;
        this._debug.add(bottom);

        let top=new THREE.Object3D();
        let t=b.clone();
        t.rotation.z=d90*2;
        top.add(t);
        let t1=b.clone();
        t1.rotation.z=d90*2;
        t1.rotation.y=d90;
        top.add(t1);
        top.position.y=1;
        this._debug.add(top);

        let h=this._props.halfHeight;
        let r=this._props.radius;
        this._debug.children[0].scale.set(r,h,r);
        this._debug.children[1].scale.set(r,r,r);
        this._debug.children[1].position.y=-h;
        this._debug.children[2].scale.set(r,r,r);
        this._debug.children[2].position.y=h;
        this.add(this._debug);
    }

    /**
     * 销毁调试
     * @private
     */
    _destroyDebug(){
        if(!this._debug)return;
        this.remove(this._debug);
        this._debug.traverse((c)=>{
            if(c instanceof THREE.Line){
                c.geometry.dispose();
                c.material.dispose();
            }
        });
        this._debug=null;
    }

    /**
     * 初始化属性
     * @private
     */
    _initProps(){
        super._initProps();
        this._props.halfHeight=this._values.halfHeight||1;
        this._props.radius=this._values.radius||0.5;
    }

    /**
     * 初始化变量
     * @private
     */
    _initVariable(){
        super._initVariable();
        this._box=new THREE.Box3();
        this._sphere=new THREE.Sphere();
        this._bottomCenter=new THREE.Vector3();
        this._topCenter=new THREE.Vector3();
        this._rayPoint=new THREE.Vector3();
        this._segPoint=new THREE.Vector3();
        this._invMat=new THREE.Matrix4();
        this._ray=new THREE.Ray();
        this._tV0=new THREE.Vector3();
        this._tS0=new THREE.Sphere();
    }

    /**
     * 初始化形状
     * @private
     */
    _initShape(){
        let h=this._props.halfHeight;
        let r=this._props.radius;
        this._bottomCenter.set(0,-h,0);
        this._topCenter.set(0,h,0);
        this._box.min.set(-r,-h-r,-r);
        this._box.max.set(r,h+r,r);
        this._sphere.radius=h+r;
    }

    /**
     * 初始化射线检测函数
     * @private
     */
    _initRaycast() {
        this._raycast=(raycaster,intersects,target)=>{
                let object=this;
                let matrixWorld = this.matrixWorld;
                if ( raycaster.ray.intersectsSphere( this.sphere.applyMatrix4(matrixWorld) ) === false ) return;
                // this._invMat.getInverse( matrixWorld );20210824
                this._invMat.copy( matrixWorld ).invert();
                this._ray.copy( raycaster.ray ).applyMatrix4( this._invMat );
                let disSq=this._ray.distanceSqToSegment(this._bottomCenter,this._topCenter,this._rayPoint,this._segPoint);
                let rsq=this._props.radius*this._props.radius;
                if(disSq<rsq){
                    let rsl=this._rayPoint.distanceToSquared(this._segPoint);
                    let ril=rsq-rsl;

                    /**
                     * 210508
                     */
                    // this._tV0.copy(raycaster.ray.direction).negate();
                    this._tV0.copy(this._ray.direction).negate();

                    let vl=Math.sqrt(ril/(1-this._tV0.y*this._tV0.y));
                    let intersectPoint=new THREE.Vector3();
                    intersectPoint.addVectors(this._rayPoint,this._tV0.multiplyScalar(vl));
                    if(intersectPoint.y>this._props.halfHeight){
                        this._tS0.set(this._topCenter,this._props.radius);
                        this._ray.intersectSphere(this._tS0,intersectPoint);
                    }else if(intersectPoint.y<-this._props.halfHeight){
                        this._tS0.set(this._bottomCenter,this._props.radius);
                        this._ray.intersectSphere(this._tS0,intersectPoint);
                    }
                    intersectPoint.applyMatrix4(matrixWorld);
                    let distance=raycaster.ray.origin.distanceTo(intersectPoint);
                    intersects.push({
                        distance:distance,
                        distanceToRay:0,
                        index: null,
                        face: null,
                        point:intersectPoint,
                        object:target?target:object,
                        shape:object,
                    });
                }
            };
    }
}